﻿#region using
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Xml;
using System.Xml.Schema;
#endregion

namespace XmlGenSharp.Logic.Main
{
  public class EnumGenerator : GeneratorBase
  {
    // Constructors

    #region EnumGenerator(string inputFileName, string defaultNamespace, XmlSchemaSimpleType simpleType)
    public EnumGenerator(string inputFileName, string defaultNamespace, XmlSchemaSimpleType simpleType)
      : base(inputFileName, defaultNamespace)
    {
      SimpleType = simpleType;
    }
    #endregion

    // Public Properties

    #region SimpleType
    public XmlSchemaSimpleType SimpleType
    {
      get;
      protected set;
    }
    #endregion

    // Public Methods

    #region GetFileName()
    public override string GetFileName()
    {
      string baseName = Path.GetFileNameWithoutExtension(InputFileName);

      return baseName + ".Class." + SimpleType.Name + ".Generator.cs";
    }
    #endregion

    #region Generate(IGeneratorContext cx)
    public override void Generate(IGeneratorContext cx)
    {
      XmlSchemaSimpleTypeRestriction restriction = SimpleType.Content as XmlSchemaSimpleTypeRestriction;
      if (restriction == null)
        return;

      cx.W(PreText);

      cx.WL("");
      cx.WL("  public partial class {0}", SimpleType.Name);
      cx.WL("  {");

      GenerateConstants(cx);
      GenerateConstructors(cx);
      GenerateProperties(cx);
      GenerateMethods(cx);
      GenerateStaticMethods(cx);
      GenerateOperators(cx);

      cx.WL("  }");

      cx.W(PostText);
    }
    #endregion

    // Private Methods

    #region GenerateConstants(IGeneratorContext cx)
    private void GenerateConstants(IGeneratorContext cx)
    {
      XmlSchemaSimpleTypeRestriction restriction = SimpleType.Content as XmlSchemaSimpleTypeRestriction;
      if (restriction == null)
        return;

      cx.WL("    // Public Constants");
      cx.WL("");
      cx.WL("    #region Public Constants");

      int val = 1;

      foreach (XmlSchemaObject obj in restriction.Facets)
      {
        XmlSchemaEnumerationFacet enumFacet = obj as XmlSchemaEnumerationFacet;
        if (enumFacet == null)
          continue;

        cx.WL("    public const int {0}Val = {1};", enumFacet.Value, val++);
      }

      cx.WL("");
      cx.WL("    public static readonly {0} Unknown = new {0}(\"Unknown\", 0);", SimpleType.Name);

      foreach (XmlSchemaObject obj in restriction.Facets)
      {
        XmlSchemaEnumerationFacet enumFacet = obj as XmlSchemaEnumerationFacet;
        if (enumFacet == null)
          continue;

        cx.WL("    public static readonly {0} {1} = new {0}(\"{1}\", {1}Val);", SimpleType.Name, enumFacet.Value);
      }

      cx.WL("    #endregion");
    }
    #endregion

    #region GenerateConstructors(IGeneratorContext cx)
    private void GenerateConstructors(IGeneratorContext cx)
    {
      cx.WL("");
      cx.WL("    // Public Constructors");
      cx.WL("");
      cx.WL("    #region {0}(string text, int val)", SimpleType.Name);
      cx.WL("    public {0}(string text, int val)", SimpleType.Name);
      cx.WL("    {");
      cx.WL("      Text = text;");
      cx.WL("      Value = val;");
      cx.WL("    }");
      cx.WL("    #endregion");
    }
    #endregion

    #region GenerateProperties(IGeneratorContext cx)
    private void GenerateProperties(IGeneratorContext cx)
    {
      cx.WL("");
      cx.WL("    // Public Properties");
      cx.WL("");

      cx.WL("    #region Text");
      cx.WL("    public string Text");
      cx.WL("    {");
      cx.WL("      get;");
      cx.WL("      protected set;");
      cx.WL("    }");
      cx.WL("    #endregion");

      cx.WL("");

      cx.WL("    #region Value");
      cx.WL("    public int Value");
      cx.WL("    {");
      cx.WL("      get;");
      cx.WL("      protected set;");
      cx.WL("    }");
      cx.WL("    #endregion");
    }
    #endregion

    #region GenerateMethods(IGeneratorContext cx)
    private void GenerateMethods(IGeneratorContext cx)
    {
      cx.WL("");
      cx.WL("    // Public Methods");

      cx.WL("");
      cx.WL("    #region ToString()");
      cx.WL("    public override string ToString()");
      cx.WL("    {");
      cx.WL("      return Text;");
      cx.WL("    }");
      cx.WL("    #endregion");

      cx.WL("");
      cx.WL("    #region GetHashCode()");
      cx.WL("    public override int GetHashCode()");
      cx.WL("    {");
      cx.WL("      return Value;");
      cx.WL("    }");
      cx.WL("    #endregion");

      cx.WL("");
      cx.WL("    #region Equals(object o)");
      cx.WL("    public override bool Equals(object o)");
      cx.WL("    {");
      cx.WL("      if (o.GetType() == typeof(int))");
      cx.WL("        return this.Value == (int) o;");
      cx.WL("");
      cx.WL("      if (o.GetType() == typeof(string))");
      cx.WL("        return this.Text == (string) o;");
      cx.WL("");
      cx.WL("      if (o.GetType() == typeof(" + SimpleType.Name + "))");
      cx.WL("        return this.Value == ((" + SimpleType.Name + ") o).Value;");
      cx.WL("");
      cx.WL("      return false;");
      cx.WL("    }");
      cx.WL("    #endregion");
    }
    #endregion

    #region GenerateStaticMethods(IGeneratorContext cx)
    private void GenerateStaticMethods(IGeneratorContext cx)
    {
      XmlSchemaSimpleTypeRestriction restriction = SimpleType.Content as XmlSchemaSimpleTypeRestriction;
      if (restriction == null)
        return;

      cx.WL("");
      cx.WL("    // Public Static Methods");

      cx.WL("");
      cx.WL("    #region Parse(string text)");
      cx.WL("    public static {0} Parse(string text)", SimpleType.Name);
      cx.WL("    {");
      cx.WL("      if (text == null)");
      cx.WL("        return Unknown;");
      cx.WL("");
      cx.WL("      switch (text.ToLower())");
      cx.WL("      {");

      foreach (XmlSchemaObject obj in restriction.Facets)
      {
        XmlSchemaEnumerationFacet enumFacet = obj as XmlSchemaEnumerationFacet;
        if (enumFacet == null)
          continue;

        cx.WL("        case \"{0}\":", enumFacet.Value.ToLower());
        cx.WL("          return {0};", enumFacet.Value);
        cx.WL("");
      }

      cx.WL("        default:");
      cx.WL("          return Unknown;");
      cx.WL("      }");
      cx.WL("    }");
      cx.WL("    #endregion");

      cx.WL("");
      cx.WL("    #region Find(string text)");
      cx.WL("    public static {0} Find(string text)", SimpleType.Name);
      cx.WL("    {");
      cx.WL("      return Parse(text);");
      cx.WL("    }");
      cx.WL("    #endregion");

      cx.WL("");
      cx.WL("    #region Find(int val)");
      cx.WL("    public static {0} Find(int val)", SimpleType.Name);
      cx.WL("    {");
      cx.WL("      switch (val)");
      cx.WL("      {");

      int val = 1;

      foreach (XmlSchemaObject obj in restriction.Facets)
      {
        XmlSchemaEnumerationFacet enumFacet = obj as XmlSchemaEnumerationFacet;
        if (enumFacet == null)
          continue;

        cx.WL("        case {0}:", val++);
        cx.WL("          return {0};", enumFacet.Value);
        cx.WL("");
      }

      cx.WL("        default:");
      cx.WL("          return Unknown;");
      cx.WL("      }");
      cx.WL("    }");
      cx.WL("    #endregion");
    }
    #endregion

    #region GenerateOperators(IGeneratorContext cx)
    private void GenerateOperators(IGeneratorContext cx)
    {
      cx.WL("");
      cx.WL("    // Public Operators");

      cx.WL("");
      cx.WL("    #region operator {0}(int val)", SimpleType.Name);
      cx.WL("    public static implicit operator {0}(int val)", SimpleType.Name);
      cx.WL("    {");
      cx.WL("      return Find(val);");
      cx.WL("    }");
      cx.WL("    #endregion");

      cx.WL("");
      cx.WL("    #region operator {0}(string val)", SimpleType.Name);
      cx.WL("    public static implicit operator {0}(string val)", SimpleType.Name);
      cx.WL("    {");
      cx.WL("      return Find(val);");
      cx.WL("    }");
      cx.WL("    #endregion");

      cx.WL("");
      cx.WL("    #region operator int({0} val)", SimpleType.Name);
      cx.WL("    public static implicit operator int({0} val)", SimpleType.Name);
      cx.WL("    {");
      cx.WL("      return val.Value;");
      cx.WL("    }");
      cx.WL("    #endregion");

      cx.WL("");
      cx.WL("    #region operator string({0} val)", SimpleType.Name);
      cx.WL("    public static implicit operator string({0} val)", SimpleType.Name);
      cx.WL("    {");
      cx.WL("      return val.Text;");
      cx.WL("    }");
      cx.WL("    #endregion");

      cx.WL("");
      cx.WL("    #region operator ==({0} lhs, {0} rhs)", SimpleType.Name);
      cx.WL("    public static bool operator ==({0} lhs, {0} rhs)", SimpleType.Name);
      cx.WL("    {");
      cx.WL("      return lhs.Value == rhs.Value;");
      cx.WL("    }");
      cx.WL("    #endregion");

      cx.WL("");
      cx.WL("    #region operator !=({0} lhs, {0} rhs)", SimpleType.Name);
      cx.WL("    public static bool operator !=({0} lhs, {0} rhs)", SimpleType.Name);
      cx.WL("    {");
      cx.WL("      return lhs.Value != rhs.Value;");
      cx.WL("    }");
      cx.WL("    #endregion");

      cx.WL("");
      cx.WL("    #region operator ==({0} lhs, int rhs)", SimpleType.Name);
      cx.WL("    public static bool operator ==({0} lhs, int rhs)", SimpleType.Name);
      cx.WL("    {");
      cx.WL("      return lhs.Value == rhs;");
      cx.WL("    }");
      cx.WL("    #endregion");

      cx.WL("");
      cx.WL("    #region operator !=({0} lhs, int rhs)", SimpleType.Name);
      cx.WL("    public static bool operator !=({0} lhs, int rhs)", SimpleType.Name);
      cx.WL("    {");
      cx.WL("      return lhs.Value != rhs;");
      cx.WL("    }");
      cx.WL("    #endregion");

      cx.WL("");
      cx.WL("    #region operator ==({0} lhs, string rhs)", SimpleType.Name);
      cx.WL("    public static bool operator ==({0} lhs, string rhs)", SimpleType.Name);
      cx.WL("    {");
      cx.WL("      return lhs.Text.ToLower() == rhs.ToLower();");
      cx.WL("    }");
      cx.WL("    #endregion");

      cx.WL("");
      cx.WL("    #region operator !=({0} lhs, string rhs)", SimpleType.Name);
      cx.WL("    public static bool operator !=({0} lhs, string rhs)", SimpleType.Name);
      cx.WL("    {");
      cx.WL("      return lhs.Text.ToLower() != rhs.ToLower();");
      cx.WL("    }");
      cx.WL("    #endregion");
    }
    #endregion
  }
}
